/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.ui.editors;

import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.commands.operations.UndoContext;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.forms.editor.FormEditor;
import org.zoundry.gaedt.core.models.application.ZGaeApplicationModel;
import org.zoundry.gaedt.core.util.ZGaeUtil;


/**
 * Form editor for editing an application.  This entails actually editing
 * three files at the same time: app.yaml, gae.yaml.
 */
public class ZGaeApplicationEditor extends FormEditor
{
    /** The application overview page. */
    private ZGaeApplicationOverviewPage mOverviewPage;
    /** The application handlers page. */
    private ZGaeApplicationHandlersPage mHandlersPage;
    /** The app.yaml source tab editor part. */
    private TextEditor mAppYamlEditorPart;
    /** The gae.yaml source tab editor part. */
    private TextEditor mGaeYamlEditorPart;
    /** The editor input for the app.yaml tab. */
    private ZGaeSourceEditorInputBase mAppYamlEditorInput;
    /** The editor input for the app.yaml tab. */
    private ZGaeSourceEditorInputBase mGaeYamlEditorInput;

    /** The application model. */
    private ZGaeApplicationModel mModel;
    /** The editor's undo context. */
    private IUndoContext mUndoContext;
    /** The next operation in the undo history when this editor was saved. */
    private IUndoableOperation mDirtyOperation;

    /**
     * C'tor.
     */
    public ZGaeApplicationEditor()
    {
        setUndoContext(new UndoContext());
    }

    /**
     * @see org.eclipse.ui.forms.editor.FormEditor#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
     */
    @Override
    public void init(IEditorSite aSite, IEditorInput aInput) throws PartInitException
    {
        super.init(aSite, aInput);

        ZGaeApplicationEditorActionBarContributor actionBarContributor = (ZGaeApplicationEditorActionBarContributor)
        getEditorSite().getActionBarContributor();
        actionBarContributor.init(this);

        // Whenever an undo or redo happens, the dirty status of the editor may have changed.
        getOperationHistory().addOperationHistoryListener(new IOperationHistoryListener()
        {
            public void historyNotification(OperationHistoryEvent aEvent)
            {
                editorDirtyStateChanged();
            }
        });
    }

    /**
     * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
     */
    @Override
    protected void addPages()
    {
        loadModel();

        setEditorTitle();

        setOverviewPage(new ZGaeApplicationOverviewPage(this));
        setHandlersPage(new ZGaeApplicationHandlersPage(this));
        setAppYamlEditorPart(new ZGaeApplicationSourceEditorPage(this));
        setGaeYamlEditorPart(new ZGaeApplicationSourceEditorPage(this));

        // Set the app.yaml editor input
        ZGaeSourceEditorInputBase appYamlEditorInput = new ZGaeAppYamlSourceEditorInput();
        appYamlEditorInput.setInput(getModel());
        setAppYamlEditorInput(appYamlEditorInput);
        // Set the gae.yaml editor input
        ZGaeSourceEditorInputBase gaeYamlEditorInput = new ZGaeGaeYamlSourceEditorInput();
        gaeYamlEditorInput.setInput(getModel());
        setGaeYamlEditorInput(gaeYamlEditorInput);

        try
        {
            addPage(getOverviewPage());
            addPage(getHandlersPage());
            int appYamlPageIndex = addPage(getAppYamlEditorPart(), getAppYamlEditorInput());
            int gaeYamlPageIndex = addPage(getGaeYamlEditorPart(), getGaeYamlEditorInput());

            setPageText(appYamlPageIndex, getModel().getAppYaml().getName());
            setPageText(gaeYamlPageIndex, getModel().getGaeYaml().getName());
        }
        catch (PartInitException e)
        {
            ZGaeUtil.logError(e);
        }
    }

    /**
     * Sets the title of the editor to the name of the application.
     */
    protected void setEditorTitle()
    {
        String appName = getModel().getName();
        if (ZGaeUtil.isNullOrEmpty(appName))
            appName = "GAE Application (unnamed)";
        setPartName(appName);
    }

    /**
     * Loads the model.
     */
    protected void loadModel()
    {
        IFile file = getFile();
        ZGaeApplicationModel model = new ZGaeApplicationModel(file.getParent());
        setModel(model);
    }

    /**
     * Utility method to get the current editor input file with the proper
     * casting.
     *
     * @return IFile
     */
    protected final IFile getFile()
    {
        return (IFile) getEditorInput().getAdapter(IFile.class);
    }

    /**
     * @see org.eclipse.ui.forms.editor.FormEditor#pageChange(int)
     */
    @Override
    protected void pageChange(int aNewPageIndex)
    {
        int appYamlPageIndex = this.pages.indexOf(getAppYamlEditorPart());
        int gaeYamlPageIndex = this.pages.indexOf(getGaeYamlEditorPart());

        if (aNewPageIndex == appYamlPageIndex)
        {
            updateAppYamlSourcePage();
        }
        else if (aNewPageIndex == gaeYamlPageIndex)
        {
            updateGaeYamlSourcePage();
        }

        super.pageChange(aNewPageIndex);
    }

    /**
     * Updates the gae.yaml source page.
     */
    protected void updateGaeYamlSourcePage()
    {
        getGaeYamlEditorInput().setInput(getModel());
        getGaeYamlEditorPart().setInput(getGaeYamlEditorInput());
    }

    /**
     * Updates the app.yaml source page.
     */
    protected void updateAppYamlSourcePage()
    {
        getAppYamlEditorInput().setInput(getModel());
        getAppYamlEditorPart().setInput(getAppYamlEditorInput());
    }

    /**
     * @see org.eclipse.ui.forms.editor.FormEditor#isDirty()
     */
    @Override
    public boolean isDirty()
    {
        IUndoableOperation undoOperation = getOperationHistory().getUndoOperation(getUndoContext());
        return undoOperation != mDirtyOperation;
    }

    /**
     * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    public void doSave(IProgressMonitor aMonitor)
    {
        try
        {
            getModel().save();
            mDirtyOperation = getOperationHistory().getUndoOperation(getUndoContext());
            editorDirtyStateChanged();
        }
        catch (Exception e)
        {
            ZGaeUtil.logAndDisplayError(e);
        }
    }

    /**
     * @see org.eclipse.ui.part.EditorPart#doSaveAs()
     */
    @Override
    public void doSaveAs()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
     */
    @Override
    public boolean isSaveAsAllowed()
    {
        return false;
    }

    /**
     * @return the overviewPage
     */
    protected ZGaeApplicationOverviewPage getOverviewPage()
    {
        return mOverviewPage;
    }

    /**
     * @param aOverviewPage the overviewPage to set
     */
    protected void setOverviewPage(ZGaeApplicationOverviewPage aOverviewPage)
    {
        mOverviewPage = aOverviewPage;
    }

    /**
     * @return the appYamlEditorPart
     */
    protected TextEditor getAppYamlEditorPart()
    {
        return mAppYamlEditorPart;
    }

    /**
     * @param aAppYamlEditorPart the appYamlEditorPart to set
     */
    protected void setAppYamlEditorPart(TextEditor aAppYamlEditorPart)
    {
        mAppYamlEditorPart = aAppYamlEditorPart;
    }

    /**
     * @return the gaeYamlEditorPart
     */
    protected TextEditor getGaeYamlEditorPart()
    {
        return mGaeYamlEditorPart;
    }

    /**
     * @param aGaeYamlEditorPart the gaeYamlEditorPart to set
     */
    protected void setGaeYamlEditorPart(TextEditor aGaeYamlEditorPart)
    {
        mGaeYamlEditorPart = aGaeYamlEditorPart;
    }

    /**
     * @return the model
     */
    protected ZGaeApplicationModel getModel()
    {
        return mModel;
    }

    /**
     * @param aModel the model to set
     */
    protected void setModel(ZGaeApplicationModel aModel)
    {
        mModel = aModel;
    }

    /**
     * @return the appYamlEditorInput
     */
    protected ZGaeSourceEditorInputBase getAppYamlEditorInput()
    {
        return mAppYamlEditorInput;
    }

    /**
     * @param aAppYamlEditorInput the appYamlEditorInput to set
     */
    protected void setAppYamlEditorInput(ZGaeSourceEditorInputBase aAppYamlEditorInput)
    {
        mAppYamlEditorInput = aAppYamlEditorInput;
    }

    /**
     * @return the gaeYamlEditorInput
     */
    protected ZGaeSourceEditorInputBase getGaeYamlEditorInput()
    {
        return mGaeYamlEditorInput;
    }

    /**
     * @param aGaeYamlEditorInput the gaeYamlEditorInput to set
     */
    protected void setGaeYamlEditorInput(ZGaeSourceEditorInputBase aGaeYamlEditorInput)
    {
        mGaeYamlEditorInput = aGaeYamlEditorInput;
    }

    /**
     * @return the undoContext
     */
    protected IUndoContext getUndoContext()
    {
        return mUndoContext;
    }

    /**
     * Gets the operation history.
     */
    protected IOperationHistory getOperationHistory()
    {
        return getSite().getWorkbenchWindow().getWorkbench().getOperationSupport().getOperationHistory();
    }

    /**
     * @param aUndoContext the undoContext to set
     */
    protected void setUndoContext(IUndoContext aUndoContext)
    {
        mUndoContext = aUndoContext;
    }

    /**
     * @return the dirtyOperation
     */
    protected IUndoableOperation getDirtyOperation()
    {
        return mDirtyOperation;
    }

    /**
     * @param aDirtyOperation the dirtyOperation to set
     */
    protected void setDirtyOperation(IUndoableOperation aDirtyOperation)
    {
        mDirtyOperation = aDirtyOperation;
    }

    /**
     * @return the handlersPage
     */
    protected ZGaeApplicationHandlersPage getHandlersPage()
    {
        return mHandlersPage;
    }

    /**
     * @param aHandlersPage the handlersPage to set
     */
    protected void setHandlersPage(ZGaeApplicationHandlersPage aHandlersPage)
    {
        mHandlersPage = aHandlersPage;
    }
}
